<?php
class Upload_files
{
	public $upload_tmp_path = '';
	public $allowed_types = ''; // Empty means everything can be uploaded
	public $max_file_size = 0; // 0 = no limit
	public $max_filename_length = 0; // 0 = no limit
	
	public $error_msg = array();
	
	public $file_temp		= '';
	public $file_name		= '';
	public $file_type		= '';
	public $file_size		= '';
	public $file_ext		= '';
	
	public $mimes			= array();
	
	public $image_width		= 0;
	public $image_height	= 0;
	
	public $finalArray 		= array();
	public $file_list		= array();
	
	function __construct($props = array())
    {
		if (count($props) > 0)
		{
			$this->initialize($props);
		}
		
		log_message('debug', "Upload Files Library Initialized");
    }
	
	/**
	 * Initialize preferences
	 *
	 * @access	public
	 * @param	array
	 * @return	void
	 */	
	public function initialize($config = array())
	{
		$defaults = array(
							'allowed_types'		=> '',
							'upload_tmp_path' 	=> '',
							'file_temp'			=> '',
							'file_name'			=> '',
							'file_type'			=> '',
							'file_size'			=> '',
							'file_ext'			=> '',
							'max_filename_length' => '',
							'max_file_size' => '',
						);	
	
	
		foreach ($defaults as $key => $val)
		{
			if (isset($config[$key]))
			{
				$method = 'set_'.$key;
				if (method_exists($this, $method))
				{
					$this->$method($config[$key]);
				}
				else
				{
					$this->$key = $config[$key];
				}			
			}
			else
			{
				$this->$key = $val;
			}
		}
	}
	
	/**
	 * Perform the file upload
	 *
	 * @access	public
	 * @param	string
	 * @return	bool
	 */	
	public function do_upload($field)
	{
		// Is $_FILES[$field] set? If not, no reason to continue.
		if (isset($_FILES[$field]['name'][0]) == FALSE)
		{
			$this->set_error('upload_no_file_selected');
			return FALSE;
		}
		else
		{
			$uploadedFiles = array();
			//	Loop over array and clean out all items that contain error code 4 aka no file uploaded :S
			foreach($_FILES[$field]['error'] AS $key => $value)
			{
				if ($value !== 4)
				{
					$uploadedFiles['name'][] = $_FILES[$field]['name'][$key];
					$uploadedFiles['type'][] = $_FILES[$field]['type'][$key];
					$uploadedFiles['tmp_name'][] = $_FILES[$field]['tmp_name'][$key];
					$uploadedFiles['error'][] =$_FILES[$field]['error'][$key];
					$uploadedFiles['size'][] = $_FILES[$field]['size'][$key];
				}
			}
			
			if (isset($uploadedFiles['name']) == FALSE)
			{
				$this->set_error('upload_no_file_selected');
				return FALSE;
			}

			$num_files = count($uploadedFiles['name']);
            $file_list = array();
            $error_hold = array();
            $error_upload = FALSE;
		}
		
		for ($i=0; $i < $num_files; $i++)
		{
			$error_hold[$i] = FALSE;

			// Was the file able to be uploaded? If not, determine the reason why.
			if (is_uploaded_file($uploadedFiles['tmp_name'][$i]) == FALSE)
			{
				$error = isset($uploadedFiles['error'][$i]) == FALSE ? 4 : $uploadedFiles['error'][$i];

				switch($error)
				{
					case 1:	// UPLOAD_ERR_INI_SIZE
						$error_hold[$i] = 'upload_file_exceeds_limit';
					break;
					case 2: // UPLOAD_ERR_FORM_SIZE
						$error_hold[$i] = 'upload_file_exceeds_form_limit';
					break;
					case 3: // UPLOAD_ERR_PARTIAL
						$error_hold[$i] = 'upload_file_partial';
					break;
					case 4: // UPLOAD_ERR_NO_FILE
						$error_hold[$i] = 'upload_no_file_selected';
					break;
					case 6: // UPLOAD_ERR_NO_TMP_DIR
						$error_hold[$i] = 'upload_no_temp_directory';
					break;
					case 7: // UPLOAD_ERR_CANT_WRITE
						$error_hold[$i] = 'upload_unable_to_write_file';
					break;
					case 8: // UPLOAD_ERR_EXTENSION
						$error_hold[$i] = 'upload_stopped_by_extension';
					break;
					default :
						$error_hold[$i] = 'upload_no_file_selected';
					break;
				}

				//return FALSE;
				$error_upload = TRUE;
			}
			
			if ($error_upload == FALSE)
			{
				// Set the uploaded data as class variables
				$this->file_temp = $uploadedFiles['tmp_name'][$i];
				$this->file_name = MD5(mt_rand().time().$uploadedFiles['name'][$i]).'_'.$this->_prep_filename(str_replace($this->get_extension($uploadedFiles['name'][$i]), strtolower($this->get_extension($uploadedFiles['name'][$i])), $uploadedFiles['name'][$i]));
				$this->file_size = $uploadedFiles['size'][$i];		
				$this->file_type = strtolower(preg_replace("/^(.+?);.*$/", "\\1", $uploadedFiles['type'][$i]));
				$this->file_ext	 = strtolower($this->get_extension($uploadedFiles['name'][$i]));

				// Convert the file size to kilobytes
				if ($this->file_size > 0)
				{
					$this->file_size = round($this->file_size/1024, 2);
				}
				
				// Is the file type allowed to be uploaded?
				if ($this->is_allowed_filetype() == FALSE)
				{
					$error_hold[$i] = 'upload_invalid_filetype';
				}
				
				// Is the file size within the allowed maximum?
				if ($this->is_allowed_filesize() == FALSE)
				{
					$error_hold[$i] = 'upload_invalid_filesize';
				}

				// Sanitize the file name for security
				$this->file_name = $this->clean_file_name($this->file_name);

				// Truncate the file name if it's too long
				if ($this->max_filename_length > 0)
				{
					$this->file_name = $this->limit_filename_length($this->file_name, $this->max_filename_length);
				}
				
				if ($this->multiple_image_properties($this->file_temp) == FALSE)
				{
					$error_hold[$i] = 'upload_invalid_filetype';
				}
				else
				{
					//	Move the file to the final destination
					if (@move_uploaded_file($this->file_temp, $this->upload_tmp_path.$this->file_name) == FALSE)
					{
						$error_hold[$i] = 'upload_destination_error';
					}
				}
			}
			
			if ($error_hold[$i])
			{
                $error_upload = TRUE;
            }
			else
			{
				$this->file_list[] = array(
                    'name' => $this->file_name,
                    'file' => $this->upload_tmp_path.$this->file_name,
                    'size' => $this->file_size,
                    'ext' => $this->file_ext,
                    'height' => $this->image_height,
					'width' => $this->image_width
                );
				//create thumbnail check if real upload image width < 278
				
				$config['source_image']=$this->upload_tmp_path.$this->file_name;
				$config['create_thumb']=TRUE;
				$config['maintain_ratio']=FALSE;
				if($this->image_width < 278 && $this->image_height < 137){
				
					$config['height']=$this->image_height;
					$config['width']=$this->image_width;
					
				}elseif($this->image_width < 278 && $this->image_height > 137){
				
					$config['height']=137;
					$config['width']=$this->image_width;
					
				}elseif($this->image_width > 278 && $this->image_height < 137){
				
					$config['height']=$this->image_height;
					$config['width']=278;
				
				}else{
					
					$config['height']=137;
					$config['width']=278;
					
				}
					$CI =& get_instance();
					$CI->load->library('image_lib', $config); //load library
					$CI->image_lib->resize(); //generating thumb
            }
			
			$this->set_data();
		}
		
		if ($error_upload)
		{
            $this->set_error($error_hold);
            return FALSE;
        }
		else
		{
            return $this->file_list;
			
        } 
	}
	
	/**
	 * Set an error message
	 *
	 * @access	public
	 * @param	string
	 * @return	void
	 */	
	public function set_error($msg)
	{
		$CI =& get_instance();	
		$CI->lang->load('upload');
		
		if (is_array($msg))
		{
			foreach ($msg as $val)
			{
				$msg = ($CI->lang->line($val) == FALSE) ? $val : $CI->lang->line($val);				
				$this->error_msg[] = $msg;
				log_message('error', $msg);
			}		
		}
		else
		{
			$msg = ($CI->lang->line($msg) == FALSE) ? $msg : $CI->lang->line($msg);
			$this->error_msg[] = $msg;
			log_message('error', $msg);
		}
	}
	
	/**
	 * Display the error message
	 *
	 * @access	public
	 * @param	string
	 * @param	string
	 * @return	string
	 */	
	public function display_errors($open = '<p>', $close = '</p>')
	{
		$str = '';
		foreach ($this->error_msg as $val)
		{
			$str .= $open.$val.$close;
		}
	
		return $str;
	}
	
	/**
	 * Prep Filename
	 *
	 * Prevents possible script execution from Apache's handling of files multiple extensions
	 * http://httpd.apache.org/docs/1.3/mod/mod_mime.html#multipleext
	 *
	 * @access	private
	 * @param	string
	 * @return	string
	 */
	function _prep_filename($filename)
	{
		if (strpos($filename, '.') === FALSE)
		{
			return $filename;
		}
		
		$parts		= explode('.', $filename);
		$ext		= array_pop($parts);
		$filename	= array_shift($parts);
				
		foreach ($parts as $part)
		{
			if ($this->mimes_types(strtolower($part)) === FALSE)
			{
				$filename .= '.'.$part.'_';
			}
			else
			{
				$filename .= '.'.$part;
			}
		}
		
		$filename .= '.'.$ext;
		
		return $filename;
	}
	
	/**
	 * Extract the file extension
	 *
	 * @access	public
	 * @param	string
	 * @return	string
	 */	
	function get_extension($filename)
	{
		$x = explode('.', $filename);
		return '.'.end($x);
	}
	
	/**
	 * Set Allowed File Types
	 *
	 * @access	public
	 * @param	string
	 * @return	void
	 */	
	function set_allowed_types($types)
	{
		$this->allowed_types = explode('|', $types);
	}
	
	/**
	 * List of Mime Types
	 *
	 * This is a list of mime types.  We use it to validate
	 * the "allowed types" set by the developer
	 *
	 * @access	public
	 * @param	string
	 * @return	string
	 */	
	function mimes_types($mime)
	{
		global $mimes;
	
		if (count($this->mimes) == 0)
		{
			if (@require_once(APPPATH.'config/mimes'.EXT))
			{
				$this->mimes = $mimes;
				unset($mimes);
			}
		}
	
		return ( ! isset($this->mimes[$mime])) ? FALSE : $this->mimes[$mime];
	}
	
	/**
	 * Verify that the filetype is allowed
	 *
	 * @access	public
	 * @return	bool
	 */	
	function is_allowed_filetype()
	{
		if (count($this->allowed_types) == 0 || is_array($this->allowed_types) == FALSE)
		{
			$this->set_error('upload_no_file_types');
			return FALSE;
		}

		foreach ($this->allowed_types as $val)
		{
			$mime = $this->mimes_types(strtolower($val));

			if (getimagesize($this->file_temp) === FALSE)
			{
				return FALSE;
			}

			if (is_array($mime))
			{
				if (in_array($this->file_type, $mime, TRUE))
				{
					return TRUE;
				}
			}
			else
			{
				if ($mime == $this->file_type)
				{
					return TRUE;
				}	
			}		
		}
		
		return FALSE;
	}
	
	/**
	 * Verify that the file is within the allowed size
	 *
	 * @access	public
	 * @return	bool
	 */	
	function is_allowed_filesize()
	{
		if ($this->max_file_size != 0 && $this->file_size > $this->max_file_size)
		{
			return FALSE;
		}
		else
		{
			return TRUE;
		}
	}
	
	/**
	 * Clean the file name for security
	 *
	 * @access	public
	 * @param	string
	 * @return	string
	 */		
	function clean_file_name($filename)
	{
		//	Remove and save MD5 hash from filename for later usage
		$filenameMD5Hash = substr($filename, 0, 33);
		//	Get filename without hash
		$filenameClean = substr($filename, 33);
		//	Get file extension
		$fileExt = $this->get_extension($filename);
		
		$filename = str_replace($this->get_extension($filenameClean), '', $filenameClean);
	
		$search		= '-';
		$replace	= '_';

		$trans = array(
						'&\#\d+?;'				=> '',
						'&\S+?;'				=> '',
						'\s+'					=> $replace,
						'[-]'					=> $replace,
						'[^a-z0-9\-\._]'		=> '',
						$replace.'+'			=> $replace,
						$replace.'$'			=> $replace,
						'^'.$replace			=> $replace,
						'\.+$'					=> ''
					  );

		$filename = strip_tags($filename);

		foreach ($trans as $key => $val)
		{
			$filename = preg_replace("#".$key."#i", $val, $filename);
		}

		$filename = trim(stripslashes($filename));
		
		if (strlen($filename) == 0)
		{
			$filename = mt_rand().'untitled';
		}

		return $filenameMD5Hash.$filename.$fileExt;
	}
	
	/**
	 * Limit the File Name Length
	 *
	 * @access	public
	 * @param	string
	 * @return	string
	 */		
	function limit_filename_length($filename, $length)
	{
		if (strlen($filename) < $length)
		{
			return $filename;
		}
		
		//	Save MD5 hash before we remove it!
		$md5hash = substr($filename, 0, 33);
		//	Remove MD5 hash from filename
		$filename = substr($filename, 33);

		$ext = '';
		if (strpos($filename, '.') !== FALSE)
		{
			$parts		= explode('.', $filename);
			$ext		= '.'.array_pop($parts);
			$filename	= implode('.', $parts);
		}

		$filename = substr($filename, 0, $length);

		//	Loop over filename and remove _ from start and end positions if found!
		if (substr($filename, 0, 1) == '_')
		{
			$filename = substr($filename, 1);
		}

		if (substr($filename, strlen($filename)-1) == '_')
		{
			$filename = substr($filename, 0, strlen($filename)-1);
		}

		$filename = $md5hash.$filename.$ext;

		return trim($filename);
	}
	
	/**
	 * Set Image Properties
	 *
	 * Uses GD to determine the width/height/type of image
	 *
	 * @access	public
	 * @param	string
	 * @return	void
	 */	
	function multiple_image_properties($path = '')
	{
		if (getimagesize($path) == FALSE)
		{
			return FALSE;
		}
		else
		{
			$D = getimagesize($path); 
			$this->image_width		= $D['0'];
			$this->image_height		= $D['1'];
			
			return TRUE;
		}
	}
	
	function set_data()
	{
		$this->finalArray[] = array (
			'file_name'			=> $this->file_name,
			'file_type'			=> $this->file_type,
			'file_path'			=> $this->upload_tmp_path,
			'full_path'			=> $this->upload_tmp_path.$this->file_name,
			'raw_name'			=> str_replace($this->get_extension($this->file_name), '', $this->file_name),
			'file_ext'			=> $this->file_ext,
			'file_size'			=> $this->file_size,
			'image_width'		=> $this->image_width,
			'image_height'		=> $this->image_height
		);
	}
	
	function data()
	{
		return $this->finalArray;
	}
}